#include<iostream>
#include<queue>
#include<pthread.h>
using namespace std;

const int default_cap = 4;

template<class T>
class BlockQueue
{
public:
  BlockQueue(int d_cap = default_cap) :cap(d_cap)
  {
    pthread_mutex_init(&mtx,nullptr);
    pthread_cond_init(&p_cond,nullptr);
    pthread_cond_init(&c_cond,nullptr);
  }
  ~BlockQueue()
  {
    pthread_mutex_destroy(&mtx);
    pthread_cond_destroy(&p_cond);
    pthread_cond_destroy(&c_cond);
  }

  void push(T task)
  {
    pthread_mutex_lock(&mtx);
    while(task_queue.size() >= cap)
    {
      pthread_cond_wait(&p_cond,&mtx);
    }
    task_queue.push(task);
    pthread_mutex_unlock(&mtx);
    pthread_cond_signal(&c_cond);
  }
  void pop(T* task)
  {
    pthread_mutex_lock(&mtx);
    while(empty())
    {
      pthread_cond_wait(&c_cond,&mtx);
    }
    *task = task_queue.front();
    task_queue.pop();
    pthread_mutex_unlock(&mtx);
    pthread_cond_signal(&p_cond);
  }
  bool empty()
  {
    return task_queue.empty();
  }
private:
  queue<T> task_queue;
  pthread_mutex_t mtx;
  pthread_cond_t p_cond;
  pthread_cond_t c_cond;
  int cap;
};


